package arpc

import (
	"context"
	"fmt"
	"net/http"
	"sync"

	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/rest"
	"github.com/zeromicro/go-zero/rest/httpx"
)

const (
	ParseRequestError = 20000
)

type ServiceError struct {
	StatusCode int
	Code       int
	Message    string
}

func (se *ServiceError) Error() string {
	return fmt.Sprintf("status code: %d, code: %d, message: %s", se.StatusCode, se.Code, se.Message)
}

func (se *ServiceError) GenResponse(w http.ResponseWriter, err error) {
	er := &ErrorResponse{
		Code:    se.Code,
		Message: se.Message,
	}

	httpx.WriteJson(w, se.StatusCode, er)
}

type NewAPIContextFunc func() APIContext

// APIContext 接口，每个 HTTP 请求会对应一个 APIContext 实例
type APIContext interface {
	SetLogger(log Logger)           // 设置 logger
	SetContext(ctx context.Context) // 设置请求 Context

	Request() any                     // 获取请求结构体，用于收到 HTTP 请求时做请求参数校验时调用
	Handler(l *APILogic) (any, error) // API 处理函数
}

// API 业务逻辑，每个接口对应一个该类的实例
type APILogic struct {
	Context       any // 业务 Context
	Uri           string
	NewAPIContext NewAPIContextFunc // 收到 HTTP 请求后，创建请求的 APIContext 回调函数
}

// 用于创建 APILogic 使用
// f 为收到 HTTP 请求后，创建请求的 APIContext 回调函数
// ctx 为业务自定义内容
func NewAPILogic(f NewAPIContextFunc, ctx any) *APILogic {
	return &APILogic{
		Context:       ctx,
		NewAPIContext: f,
	}
}

func (l *APILogic) handler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		apictx := l.NewAPIContext()

		log := logx.WithContext(r.Context())
		apictx.SetLogger(log)
		apictx.SetContext(r.Context())

		req := apictx.Request()

		if err := httpx.Parse(r, req); err != nil {
			se := &ServiceError{
				StatusCode: http.StatusBadRequest,
				Code:       ParseRequestError,
				Message:    err.Error(),
			}

			log.Errorf("HTTP Parse Request error: %s", se.Error())
			httpx.ErrorCtx(r.Context(), w, nil, se.GenResponse)

			return
		}

		l.Uri = r.URL.Path
		resp, err := apictx.Handler(l)
		if err != nil {
			if se, ok := err.(*ServiceError); ok {
				httpx.ErrorCtx(r.Context(), w, nil, se.GenResponse)
			} else {
				httpx.ErrorCtx(r.Context(), w, err)
			}
		} else {
			httpx.OkJsonCtx(r.Context(), w, resp)
		}
	}
}

// APIServer 结构
type APIServer struct {
	server *rest.Server

	config *RestConf
	routes map[string][]rest.Route
}

// 使用已初始化的配置创建 APIServer
func CreateAPIServerWithConf(c *RestConf) *APIServer {
	s := &APIServer{
		server: rest.MustNewServer(*c),
		config: c,
		routes: map[string][]rest.Route{},
	}

	return s
}

// 注册 API 路由
// prefix 为请求 path 中的版本，如 /v1，/v2
// method 为 HTTP 请求方法，如 http.MethodGet
// path 为访问路径
// l 为请求处理的 APILogic 实例
func (s *APIServer) RegisteHandler(prefix string, method string, path string, l *APILogic) {
	s.routes[prefix] = append(s.routes[prefix], rest.Route{
		Method:  method,
		Path:    path,
		Handler: l.handler(),
	})
}

// 注册用户 HTTPHandler
// prefix 为请求 path 中的版本，如 /v1，/v2
// method 为 HTTP 请求方法，如 http.MethodGet
// path 为访问路径
// f 为用户 HTTP 请求处理函数
func (s *APIServer) RegisteUserHandler(prefix string, method string, path string, f http.HandlerFunc) {
	s.routes[prefix] = append(s.routes[prefix], rest.Route{
		Method:  method,
		Path:    path,
		Handler: f,
	})
}

// 启动 APIServer
func (s *APIServer) Run(wg *sync.WaitGroup) {
	for prefix, routes := range s.routes {
		s.server.AddRoutes(routes, rest.WithPrefix(prefix))
	}

	wg.Add(1)
	go func(wg *sync.WaitGroup) {
		defer func() {
			wg.Done()
			s.server.Stop() // 关闭日志，会将缓冲在内存中的日志写入文件
		}()

		fmt.Printf("Starting API Server at %s:%d\n", s.config.Host, s.config.Port)
		s.server.Start()
	}(wg)
}
